package kr.co.goodwilldd;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;

@Controller
@RequestMapping("/mvc/chat/{topic}/{user}")
public class ChatController {

	private static final Logger logger = LoggerFactory
			.getLogger(ChatController.class);
	private final Map<String, ChatParticipant> participants = new ConcurrentHashMap<String, ChatParticipant>();

	/**
	 * Get chat messages immediately if any are cached or later when they
	 * arrive.
	 */
	@RequestMapping(method = RequestMethod.GET, produces = "application/json")
	@ResponseBody
	public Object getMessages(@PathVariable String topic,
			@PathVariable String user) {
		List<ChatMessage> messages = null;
		DeferredResult deferredResult = null;
		try {
			ChatParticipant participant = getChatParticipant(user, topic);
			deferredResult = new DeferredResult();
			messages = participant
					.getMessagesWhenAvailable(deferredResult);
		} catch (Exception e) {
			logger.error("{}", e);
		}
		return (messages != null) ? messages : deferredResult;
	}

	/**
	 * Post a message to chat participants.
	 */
	@RequestMapping(method = RequestMethod.POST)
	@ResponseBody
	public void postMessage(ChatMessage message) {
		for (ChatParticipant participant : this.participants.values()) {
			if (message.getTopic().equals(participant.getTopic())) {
				participant.processMessage(message);
			}
		}
	}

	/**
	 * Remove a chat participant.
	 */
	@RequestMapping(method = RequestMethod.DELETE)
	@ResponseBody
	public void removeParticipant(@PathVariable String topic,
			@PathVariable String user) {
		removeChatParticipant(user, topic);
	}

	private ChatParticipant getChatParticipant(String user, String topic) {
		String key = getKey(user, topic);
		if (this.participants.containsKey(key)) {
			return this.participants.get(key);
		} else {
			ChatParticipant participant = new ChatParticipant(user, topic);
			this.participants.put(key, participant);
			return participant;
		}
	}

	private void removeChatParticipant(String user, String topic) {
		this.participants.remove(getKey(user, topic));
	}

	private String getKey(String user, String topic) {
		return user + ":" + topic;
	}
}
